package org.backend.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.backend.pojo.entity.*;
import org.backend.pojo.dto.ProductForTransactionDTO;
import org.backend.pojo.dto.TransactionCreateDTO;
import org.backend.pojo.vo.*;
import org.backend.repository.*;
import org.backend.service.TransactionService;
import org.backend.utils.ApiResponse;
import org.backend.websocket.WxWebSocketHandler;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import static org.backend.constant.OrderStatusConstants.*;
import static org.backend.constant.WebSocketTypeConstants.PURCHASE_GOODS;

@Service
@Slf4j
public class TransactionServiceImpl implements TransactionService {

    @Autowired
    private TransactionRepository transactionRepository;

    @Autowired
    private WxWebSocketHandler wxWebSocketHandler;

    @Autowired
    private ProductRepository productRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private MessageRepository messageRepository;

    @Autowired
    private AddressRepository addressRepository;

    @Override
    @Transactional
    public ApiResponse<Integer> create(TransactionCreateDTO transactionCreateDTO) {
        List<Integer> productIds = new ArrayList<>();
        // 这一段代码部分冗余 业务混杂 属性赋值不够优雅
        try {
            Transaction transaction = new Transaction();
            if (transactionCreateDTO.getOpenid() != null) {
                transaction.setOpenid(transactionCreateDTO.getOpenid());
                transactionCreateDTO.setUserId(-1);
            }
            transaction.setUserId(transactionCreateDTO.getUserId());
            transaction.setTransactionStatus(PENDING_PAYMENT); // 待付款
            BigDecimal totalAmount = new BigDecimal(0);
            for (ProductForTransactionDTO product : transactionCreateDTO.getProductList()) {
                BigDecimal price = new BigDecimal(String.valueOf(product.getPrice())); // 设置价格
                Integer quantity = product.getQuantity();
                if (quantity == null) { // 设置数量
                    quantity = 1;
                }
                productIds.add(product.getProductId()); // 获取productId列表 为websocket准备
                totalAmount = totalAmount.add(price.multiply(BigDecimal.valueOf(quantity)));
            }
            transaction.setTotalAmount(totalAmount);
            LocalDateTime startTime = LocalDateTime.now();
            transaction.setTransactionStartTime(startTime);
            transaction.setTransactionEndTime(startTime.plusDays(1)); // 订单过期时间 24小时
            transaction.setId(transactionCreateDTO.getId());

            // 设置交易项目，一个交易的商品可能不止一个
            int row1 = transactionRepository.insertTransaction(transaction);
            List<TransactionItem> transactionItems = getTransactionItems(transactionCreateDTO, transaction);
            transaction.setTransactionItems(transactionItems);

            int row2 = transactionRepository.insertTransactionItems(transaction.getTransactionItems());
            // 一团乱，少碰
            if (row1 > 0 && row2 > 0) {
                // 修改商品状态为预定
                reviseProductStatus(transaction.getTransactionId(), 3);

                log.info("订单生成成功啦");
                // 创建订单后消息通知
                // 1、通过productId查找对应的卖家(userId或者openid)
                for (Integer productId : productIds) {
                    Product product = productRepository.getProductById(productId);
                    if (product == null) {
                        throw new IllegalArgumentException("未查询到商品");
                    }
                    String openid = product.getOpenid();
                    Integer userId = product.getUserId();
                    // 2、给卖家的openid或者userId发送消息 内容(买家姓名、商品名字、商品Id、消息类别)
                    MessageVO messageVO = new MessageVO();
                    MessageVO.Data1 data = new MessageVO.Data1();
                    data.setProductId(productId);
                    data.setProductName(product.getName());
                    String formattedTime = startTime.toString().replace("T", " ");
                    data.setCreateTime(formattedTime);
                    messageVO.setType(PURCHASE_GOODS);
                    if (openid != null) { // 小程序端
                        WxUser wxUser = userRepository.findWxUserByOpenId(openid);
                        data.setBuyerName(wxUser.getName());
                        messageVO.setData(data);
                        // 新增信息保存
                        String content = "有用户下单您的商品啦";
                        Message message = new Message();
                        message.setContent(content);
                        message.setReceiverId(openid);
                        message.setSenderId(-1);
                        message.setSenderOpenid(transactionCreateDTO.getOpenid());
                        message.setType(PURCHASE_GOODS);
                        message.setSendTime(startTime);
                        message.setProductId(productId);
                        int rows = messageRepository.addMessage(message);
                        if (rows > 0) {
                            // 3、发送消息
                            wxWebSocketHandler.sendNotificationToUser(openid, content, messageVO);
                        } else {
                            throw new IllegalArgumentException("插入新消息异常");
                        }

                    } else { // 网页端
                        User user = userRepository.findUserByUserId(userId);
                        data.setBuyerName(user.getUsername());
                        messageVO.setData(data);
                        // 3、todo 网页版暂无实现
                    }
                }
                return ApiResponse.success(transaction.getTransactionId());
            }
            return ApiResponse.error("transaction creation failed");
        } catch (Exception e) {
            return ApiResponse.error("transaction creation failed");
        }
    }

    @Override
    public ApiResponse<List<TransactionWithProductsVO>> getTransaction(Transaction transaction) {
        if (transaction.getOpenid() != null) {
            transaction.setUserId(-1);
        }
        List<Transaction> transactionList;

        transactionList = transactionRepository.getTransactionById(transaction);

        List<TransactionWithProductsVO> transactionWithProductsVOList = new ArrayList<>();
        for (Transaction t : transactionList) {
            TransactionWithProductsVO transactionWithProductsVO = new TransactionWithProductsVO();
            BeanUtils.copyProperties(t, transactionWithProductsVO);
            transactionWithProductsVO.setActionText(t.getTransactionStatus());

            List<ProductWithQuantityVO> productList = transactionRepository.getTransactionWithProducts(t.getTransactionId());
            transactionWithProductsVO.setProducts(productList);
            transactionWithProductsVOList.add(transactionWithProductsVO);
        }
        log.info("\ntransactionWithProductsVOList:{}\n", transactionWithProductsVOList);
        if (!transactionWithProductsVOList.isEmpty()) {
            return ApiResponse.success(transactionWithProductsVOList);
        }
        return ApiResponse.error("transaction without products");
    }

    @Override
    public ApiResponse<TransactionWithProductsVO> getTransactionForOrder(Transaction transaction) {
        Transaction transaction1 = transactionRepository.getTransactionByTransactionId(transaction.getTransactionId());
        Address address = addressRepository.getAddressById(transaction1.getId());
        TransactionWithProductsVO transactionWithProductsVO = new TransactionWithProductsVO();
        BeanUtils.copyProperties(transaction1, transactionWithProductsVO);
        transactionWithProductsVO.setAddress(address);
        List<ProductWithQuantityVO> productList = transactionRepository.getTransactionWithProducts(transaction.getTransactionId());
        transactionWithProductsVO.setProducts(productList);
        if (transactionWithProductsVO.getProducts() != null) {
            return ApiResponse.success(transactionWithProductsVO);
        }
        return ApiResponse.error("transaction without products");
    }

    @Override
    public ApiResponse<TransactionForPayVO> getTransactionForPay(Transaction transaction) {
        int transactionId = transaction.getTransactionId();
        List<ProductWithQuantityVO> productList = transactionRepository.getTransactionWithProducts(transactionId);
        Transaction transaction1 = transactionRepository.getTransactionByTransactionId(transactionId);

        LocalDateTime startTime = LocalDateTime.now();
        LocalDateTime endTime = transaction1.getTransactionEndTime();
        Duration duration = Duration.between(startTime, endTime);
        Long totalSeconds = duration.getSeconds();

        TransactionForPayVO transactionForPayVO = new TransactionForPayVO();
        transactionForPayVO.setProductList(productList);
        transactionForPayVO.setRemainingTime(totalSeconds);

        return ApiResponse.success(transactionForPayVO);
    }

    @Override
    @Transactional
    public ApiResponse<String> delete(Transaction transaction) {
        Integer transactionId = transaction.getTransactionId();
        // 商品状态调整 取消订单后，商品状态自动调整为在售
        reviseProductStatus(transactionId, 0);
        int row = transactionRepository.deleteTransaction(transactionId);
        if (row > 0) {
            return ApiResponse.success("transaction deleted successfully");
        }
        return ApiResponse.error("transaction deletion failed");
    }

    @Override
    public ApiResponse<String> payment(Transaction transaction) {
        if (transaction.getOpenid() != null) {
            transaction.setUserId(-1);
        }
        Integer transactionId = transaction.getTransactionId();
        Transaction transaction1 = transactionRepository.getTransactionByTransactionId(transactionId);
        transaction1.setTransactionStatus(PENDING_SHIPMENT);
        transaction1.setTransactionEndTime(LocalDateTime.now());
        log.info("\ntransaction1:{}\n", transaction1);
        int row = transactionRepository.update(transaction1);
        if (row == 0) {
            return ApiResponse.error("transaction payment failed");
        }
        // 获取订单中的所有商品信息
        // todo 暂时没有确定用户发布的商品数量，默认只有一个商品，后续需要添加
        // 商品状态调整 支付成功后，商品状态自动调整为售出
        reviseProductStatus(transactionId, 1);

        // todo 支付回调等暂无

        return ApiResponse.success("transaction payment successfully");
    }


    @Override
    public List<MonthStatisticsVO> chart(String openid, int year, int month) {
        return transactionRepository.chart(openid, year, month);
    }

    @Override
    public List<CategoryStatisticsVO> categoryChart(String openid) {
        return transactionRepository.categoryChart(openid);
    }

    private void reviseProductStatus(Integer transactionId, Integer status) {
        List<ProductWithQuantityVO> list = transactionRepository.getTransactionWithProducts(transactionId);
        for (ProductWithQuantityVO productWithQuantityVO : list) {
            Product product = new Product();
            product.setProductId(productWithQuantityVO.getProductId());
            product.setType(productWithQuantityVO.getType());
            product.setStatus(status); // 0在售 1已售出 2下架 3预定
            log.info("\nproduct:{}\n", product);
            productRepository.update(product);
        }
    }

    private List<TransactionItem> getTransactionItems(TransactionCreateDTO transactionCreateDTO, Transaction transaction) {
        int transactionId = transaction.getTransactionId();
        // 设置订单中不同的商品
        List<TransactionItem> transactionItems = new ArrayList<>();
        for (ProductForTransactionDTO product : transactionCreateDTO.getProductList()) {
            TransactionItem transactionItem = new TransactionItem();
            transactionItem.setTransactionId(transactionId);
            transactionItem.setProductId(product.getProductId());
            if (product.getQuantity() == null) {
                product.setQuantity(1);
            }
            transactionItem.setQuantity(product.getQuantity());
            transactionItems.add(transactionItem);
        }
        return transactionItems;
    }
}
